import json
import math
import os
import cv2
import pandas as pd
import numpy as np
from rich.progress import Progress
from enum import Enum
from copy import deepcopy
from video_extract import config_parser, mkdir


def contiguous(arr):
    m = arr[1] - arr[0]
    for i in range(1, arr.shape[0] - 1):
        if arr[i + 1] - arr[i] != m:
            return False
    return True


def consecutive(data, stepsize=1):
    return np.split(data, np.where(np.diff(data) != stepsize)[0] + 1)


def frame_to_time(d, fps):
    def _time_convert(input, cache: list, fps):
        if fps > 0:
            interval = fps
        else:
            interval = 60
        carry, rem = divmod(input, interval)
        cache.append(rem)
        if carry == 1 or carry == 0:
            cache.append(carry)
            return cache
        else:
            return _time_convert(carry, cache, 0)

    d = _time_convert(d, list(), fps)
    [d.append(0) for _ in range(4 - len(d))]
    time = ""
    for stap in d[::-1][:-1]:
        time += f"{int(stap)}:"
    return time


def frames_to_timecode(framerate, frames):
    """
    视频 通过视频帧转换成时间
    :param framerate: 视频帧率
    :param frames: 当前视频帧数
    :return:时间（00:00:01:01）
    """
    return '{0:02d}:{1:02d}:{2:02d}:{3:02d}'.format(int(frames / (3600 * framerate)),
                                                    int(frames / (60 * framerate) % 60),
                                                    int(frames / framerate % 60),
                                                    int(frames % framerate))


class location(Enum):
    hmt = 0
    hm = 1
    mt = 2
    ht = 4

def find_nearest(array, value):
    array = np.asarray(array)
    idx = (np.abs(array - value)).argmin()
    return idx

def split_DI(tmp_I, tmp_D, times, ids,video_categories):

    def find_ids(i, ids) -> bool:
        for video_name in ids.keys():
            video_id, video_time = ids[video_name]
            if i in video_id:
                return video_id, video_name

    video_id, video_name = find_ids(tmp_I[0], ids)

    video_name_with_ids = dict()
    video_ids = list()
    for ad_name in video_categories:
        #如果该广告在某广告名称分类里
        if video_name in video_categories[ad_name]:
            for video in video_categories[ad_name]:
                video_name_with_ids.update({video:(ids[video][0], ids[video][1])})
                video_ids.extend(ids[video][0])

    def choose_video(I,D,T,video_name_with_ids):
        res = dict()

        # total_times = []
        # for key in video_name_with_ids.keys():
        #     total_times.append(video_name_with_ids[key][1])
        #
        # dis = find_nearest(total_times, fps*(D[-1]-D[0]))

        video_name = None
        for key in video_name_with_ids.keys():
            index = 0
            if I[0] in video_name_with_ids[key][0]:
                video_name = key
                break

        # video_name = sorted(res.items(), key=lambda x: x[1], reverse=True)[0][0]
        video_id = ids[video_name][0]

        h_idx = np.where(I%3==0)
        if h_idx[0].any():
            I[h_idx] = video_id[0]
        m_idx = np.where(I%3==1)
        if m_idx[0].any():
            I[m_idx] = video_id[1]
        t_idx = np.where(I%3==2)
        if t_idx[0].any():
            I[t_idx] = video_id[2]

        return I

    def check_head_in_mid(I):
        index = 0
        pre = None
        for i in I:
            if pre is not None:
                if i%3==0 and pre%3==2:
                    return index
            pre = i
            index += 1
        return None

    index = 0
    for i, d in zip(tmp_I, tmp_D):
        if int(i) not in video_ids or index == tmp_I.shape[0]-1:
            if index == tmp_I.shape[0]-1:
                index += 1
            select_I = tmp_I[:index]
            head_id = check_head_in_mid(select_I)
            if head_id is not None:
                index = head_id
            select_I, rest_I = tmp_I[:index], tmp_I[index:]
            select_D, rest_D = tmp_D[:index], tmp_D[index:]
            select_T, rest_T = times[:index], times[index:]
            select_I = choose_video(select_I,select_D,select_T,video_name_with_ids)
            return (select_I, select_D, select_T, rest_I, rest_D, rest_T), video_name
        index += 1
    return None,video_name

def location_confirm(arr):
    flag = 0
    for i in arr:
        if i % 3 == 0:
            flag += 1
        elif i % 3 == 1:
            flag += 2
        else:
            flag += 3

    if flag == 6:
        return location.hmt
    elif flag == 5:
        return location.mt
    elif flag == 3:
        return location.hm
    elif flag == 4:
        return location.ht
    else:
        return None

def check_and_split(select_D, totol_time, out):
    index = 0
    pre = None
    totol_frames = math.ceil(totol_time)
    for d in select_D:
        if index == 0:
            pre = d
        if d - pre >= totol_frames:
            cut,select_D = select_D[:index],select_D[index:]
            out.append(cut)
            check_and_split(select_D, totol_time, out)
            break
        index += 1
    if not out:
        out.append(select_D)
    return out

def frame_with_filename(result: dict, interval, threshold: float, progress) -> dict:
    tmp_I = []
    tmp_D = []
    task = progress.add_task("[red]帧和广告匹配中...", total=len(result))
    for key in result.keys():
        D = np.array(result[key])[0].flatten()
        I = np.array(result[key])[1].flatten()
        D_index_pre = np.where(D >= threshold)[0]
        I = I[D_index_pre]
        tmp_I.append(I)
        D_index = np.array(list(map(lambda x: x * interval + int(key), D_index_pre)))
        tmp_D.append(D_index)
        if not progress.finished:
            progress.update(task, advance=1)

    tmp_I, tmp_D = np.concatenate(tmp_I), np.concatenate(tmp_D)
    times = [frames_to_timecode(fps, t) for t in tmp_D]

    return tmp_I, tmp_D, times

def find_key(dct, name):
    for key in dct.keys():
        if name in dct[key]:
            return dct[key]

def parse(result: dict, ids: dict,
          fps: int, threshold: float,
          video_categories:dict, progress) -> dict:
    interval = 8

    tmp_I, tmp_D, times = frame_with_filename(result,
                                              interval,
                                              threshold,
                                              progress)

    print("parsing the data ...")

    res = dict()
    while tmp_I.any():
        if not tmp_I.any():
            break


        result, video_name = split_DI(tmp_I, tmp_D, times, ids,video_categories)
        if result is None:
            continue
        select_I, select_D, select_T, tmp_I, tmp_D, times = result
        total_time = ids[video_name][1]*fps
        arr,num = np.unique(select_I,return_counts=True)
        if arr.shape[0] < 2:
            continue

        loc = location_confirm(arr)
        if loc is None:
            continue

        videos = find_key(video_categories, video_name)
        total_times = [ids[video][1]*fps for video in videos]

        if loc in (location.hm, location.mt):
            max_total_time = max(total_times)/2
        max_total_time = total_time
        cuts = []
        #根据时长切分
        cuts = check_and_split(select_D, max_total_time, cuts)

        for cut in cuts:
            start,end = cut[0],cut[-1]
            current_time = end - start

            if loc == location.hm:
                closed_time_id = find_nearest(total_times, (current_time+0.4*fps) * 2)
                if total_time < total_times[closed_time_id]:
                    video_name = videos[closed_time_id]
                    total_time = total_times[closed_time_id]
                end += (total_time - (end - start))
            elif loc == location.mt:
                closed_time_id = find_nearest(total_times, (current_time+0.4*fps) * 2)
                if total_time < total_times[closed_time_id]:
                    video_name = videos[closed_time_id]
                    total_time = total_times[closed_time_id]
                start -= (total_time - (end - start))
                if start < 0:
                    start = 0
            else:
                closed_time_id = find_nearest(total_times, (current_time+0.8*fps))
                if total_time < total_times[closed_time_id]:
                    video_name = videos[closed_time_id]
                    total_time = total_times[closed_time_id]
                pass

            time_start = frames_to_timecode(fps, int(start))
            time_end = frames_to_timecode(fps, int(end))
            if video_name not in res.keys():
                res.update({video_name:[[time_start,time_end]]})
            else:
                res[video_name].append([time_start,time_end])

    return res


def result_to_csv(data: pd.DataFrame):
    cols = data.columns.values
    index = [i for i, name in enumerate(cols) if "播出时间" in name]
    data.insert(index[0] + 1, column="程序结果", value="")
    return data

def name_with_video(data: pd.DataFrame):
    result = dict()
    for index, row in data.iterrows():
        video = os.path.basename(row["证据下载地址"])
        name = row["广告名称"].split(" ")[0]
        if name not in result.keys():
            result.update({name:[video]})
        else:
            result[name].append(video)
    return result

if __name__ == "__main__":
    p = "config/app.conf"
    config = config_parser(p)
    excel_path = config["app"]["excel_path"]
    extract_video = config["detect"]["extract_video"]
    gallery_path = config["detect"]["gallery_path"]
    detect_path = config["detect"]["detect_path"]
    thre = float(config["parse"]["threshold"])
    res_path = mkdir(config["parse"]["result_path"])
    data = pd.read_excel(excel_path, sheet_name=0)
    data = result_to_csv(data)

    video_categories = name_with_video(data)
    json_files = [os.path.join(detect_path, name) for name in os.listdir(detect_path)]

    f = open(os.path.join(gallery_path, "gallery.json"), 'r')
    ids = json.load(f)

    time_dict = dict()

    with Progress() as progress:
        task = progress.add_task('[green]分析结果中，请稍后...', total=len(json_files))
        for file in json_files:
            cap = cv2.VideoCapture(extract_video)
            fps = float(cap.get(cv2.CAP_PROP_FPS))
            day = os.path.basename(file)[4:14]
            # if "6-20" not in day: continue
            f = open(file, 'r')
            res = json.load(f)
            res = parse(res, ids, fps, thre, video_categories, progress)

            for i, url in enumerate(data["证据下载地址"]):
                times = ""
                for key in res.keys():
                    if key in url:
                        for time in res[key]:
                            times += f"{day}-{time[0][:-3]}\t"
                if i not in time_dict.keys():
                    time_dict.update({i: [times]})
                else:
                    time_dict[i].append(times)
            if not progress.finished:
                progress.update(task, advance=1)

    print("make excel!!")
    for i in time_dict.keys():
        data._set_value(i, "程序结果", value=time_dict[i])
    data.to_excel(os.path.join(res_path, os.path.basename(excel_path)), index=False)
    print("finish")
    # res_c = dict()
    # for key in res.keys():
    #     res_c.update({name[key]:res[key]})
    #
    # with open(file[:-5]+"result.json", 'w') as f:
    #     json.dump(res_c, f, ensure_ascii=False)

